home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-10-16 | 17.1 KB | 492 lines |
- /*
- Engine class for support of masked input in IRAD.
-
- Created 3/26/98 by Paul Lancaster.
- */
-
- package com.symantec.itools.swing;
-
- import java.awt.event.KeyEvent;
- import java.awt.*;
-
- public class MaskEngine {
-
- // These constants are used to specify the data type information needed by
- // the engine to specialize mask behaviour.
- public static final int texttype = 0;
- public static final int numbtype = 1;
- public static final int datetype = 2;
- public static final int timetype = 3;
-
- public MaskEngine(String mask, int datatype) { // ctor
- init();
- setMask(mask);
- setDatatype(datatype);
- }
-
- public MaskEngine() { // default constructor
- init();
- }
-
- public void setMask(String mask) {
- //vasu start
- if(mask.trim().equals("")) return;
- //vasu end
- _mask = mask;
- _maskLength = mask.length();
-
- // Analyze filter and initialize supporting data structures
- _filterCount = 0;
- resetMaskScan();
-
- // First pass: count filters and total displayable mask characters.
- // Also mark first and last filter positions.
- for (_maskCount = 0; 0 != getNextMaskChar(); _maskCount++) {
- if (_curMaskType == filtertype) {
- _filterCount++; // count filters
- _lastFilterPos = _curMaskPos; // new "last" filter pos
- }
- if (1 == _filterCount)
- _firstFilterPos = _curMaskPos; // note first filter pos
- }
- if (_maskCount * _filterCount == 0)
- return; // error: no displayable characters or no filters in mask
-
- // Second pass: fill filter position array.
- _filterPositions = new int[_filterCount];
- _commandCorrections = new int[_filterCount];
- resetMaskScan();
- for (int i = 0, j = 0; i < _maskCount; i++) {
- getNextMaskChar();
- if (_curMaskType == filtertype) {
- _filterPositions[j] = _curMaskPos;
- _commandCorrections[j++] = _commandCorrection;
- }
- }
- }
-
- public void setDatatype(int datatype) { _datatype = datatype; }
- public String getMask() { return _mask ; }
- public int getDatatype() { return _datatype ; }
-
- /* Called to initialize the display of the masked data.
- The first parameter is the current data in the field.
- The second parameter holds the string that should be displayed.
- The return value is the initial caret position.
- */
- public int initDisplay(String data, StringBuffer newData) {
- //vasu start
- if (_filterCount == 0){ // no filters?
- newData.append(data);
- return 0; // return first position
- }
- //vasu end
- int datalen = data.length();
- int inpos = 0; // track input position
- boolean zerofill = false;
- boolean dataRightOfDecimal = false;
- boolean maskRightOfDecimal = false;
- resetMaskScan();
- char c;
- for (int i = 0; (c = getNextMaskChar()) != 0; i++) { // mask drives output
- if (_curMaskType == filtertype) { // we are on a filter?
- char z = zerofill ? '0' : '_';
- if (inpos < datalen) { // consumed all data yet?
- c = data.charAt(inpos++); // if not, get next data char
- if (numbtype == _datatype) { // special handling for numerics
- if (_decimalPoint == c) {
- c = z;
- dataRightOfDecimal = true;
- if (maskRightOfDecimal && inpos < datalen)
- c = data.charAt(inpos++);
- } else if (dataRightOfDecimal && !maskRightOfDecimal) {
- c = z;
- inpos--; // can not use this fractional digit yet
- } else if (!dataRightOfDecimal && maskRightOfDecimal) {
- c = z;
- while (inpos < datalen) // scan data for dec. pt.
- if (_decimalPoint == data.charAt(inpos++)) {
- if (inpos < datalen)
- c = data.charAt(inpos++);
- dataRightOfDecimal = true;
- break;
- }
- }
- } // if numeric field
- } else // we have consumed all input data
- c = z;
- } else if (_curMaskChar == _decimalPoint && numbtype == _datatype) {
- maskRightOfDecimal = true;
- if (0 == datalen) { // for empty numeric fields, start zero filling
- zerofill = true;
- newData.setCharAt(newData.length() - 1, '0'); // zero units digit
- }
- else if (inpos < datalen && data.charAt(inpos) == _decimalPoint) {
- inpos++; // skip decimal point in input
- dataRightOfDecimal = true;
- }
- }
- newData.append(c);
- }
-
- // Zero units digit of empty numeric fields with no decimal.
- if (numbtype == _datatype && 0 == datalen && !zerofill && _maskLength > 0)
- newData.setCharAt(newData.length() - 1, '0');
-
- return _filterPositions[0] - _commandCorrections[0];
- }
-
- /* This is the main workhorse method.
- It is called for every key stroke corresponding to displayable
- characters once editing begins.
- The 1st parameter is the user keystroke event object.
- The 2nd parameter is the current cursor position (zero based).
- The 3rd parameter is the current text from the component.
- The 4th parameter is output and is what should be displayed in the component.
- The 5th and 6th parameters are the selection start and end, respectively.
- The return value is the new cursor position within the "newData"
- parameter (zero based), unless it is negative, in which case:
- -1 means the input is inconsistent (position not in range or not on a filter.
- -2 means the input keystroke is not accepted by the relevant filter.
- */
- public int processKey(java.awt.event.KeyEvent e, int pos, String data,
- StringBuffer newData, int selStart, int selEnd) {
- newData.append(data); // init output to input
- int keyCode = e.getKeyCode();
- if (!setMaskPos(pos) && !isNavKey(keyCode))
- return -1; // cursor pos not in bounds
- char key = e.getKeyChar();
- if (key == _decimalPoint && _datatype == numbtype) { // decimal?
-
- // Move caret to first filter to the right of first decimal point
- // that is to the right of current caret position.
- while (getNextMaskChar() != 0)
- if (_curMaskChar == _decimalPoint)
- return nextFilterPos();
- return pos; // could not find rightward decimal point
- } else if (filtertype != _curMaskType) { // not on filter position?
- if (0 == pos && 0 == selStart && selEnd > _firstFilterPos)
- setMaskPos(pos = _firstFilterPos);
- else if (!isNavKey(keyCode)) {
- if (pos > _lastFilterPos)
- return -1; // beyond last filter
- nextFilterPos(); // advance to next filter
- pos = _curMaskPos - _commandCorrection;
- }
- }
- switch (keyCode) { // handle control keys
- case e.VK_BACK_SPACE:
- if (selStart < selEnd) { // if selected text exists
- if (pos != selStart || selEnd - selStart > 1) {
- clearSelectedText(selStart, selEnd, newData);
- return Math.max(selStart, _firstFilterPos);
- }
- }
- int newPos = prevFilterPos();
- if (pos > 0 && newPos != -1)
- newData.setCharAt(newPos, '_');
- return newPos != -1 ? newPos : pos;
- case e.VK_DELETE:
- if (selStart < selEnd) { // if selected text exists
- clearSelectedText(selStart, selEnd, newData);
- return Math.max(selStart, _firstFilterPos);
- }
- newData.setCharAt(pos, '_');
- return pos;
- case e.VK_LEFT:
- return prevFilterPos();
- case e.VK_RIGHT:
- return nextFilterPos();
- case e.VK_HOME:
- return _filterPositions[0] - _commandCorrections[0];
- case e.VK_END:
- return _filterPositions[_filterCount-1] - _commandCorrections[_filterCount-1];
- }
- if (matchFilter(key)) {
- clearSelectedText(selStart, selEnd, newData);
- if (eShiftLower == _shiftState)
- key = Character.toLowerCase(key);
- else if (eShiftUpper == _shiftState)
- key = Character.toUpperCase(key);
- newData.setCharAt(pos, key);
- int newpos = nextFilterPos();; // match: advance to next filter
- return newpos == -1 ? _lastFilterPos - 999 : newpos;
- } else // no match: return with no changes
- return -2;
- }
-
- public boolean stripMask(String data, StringBuffer newData) {
- if (_maskLength == 0){
- newData.append(data);
- return true;
- }
- resetMaskScan();
- boolean retval = true;
- for (int i = 0; getNextMaskChar() != 0; i++) {
- char c = data.charAt(i);
- if (_curMaskType == filtertype) {
- if (c != '_')
- newData.append(c);
- else if (isMandatory()) // empty filter position
- retval = false; // missing mandatory data
- } else if (_curMaskChar == _decimalPoint && _datatype == numbtype) {
- newData.append(_decimalPoint);
- }
- }
- return retval;
- }
-
- // Return true iff the engine handles the given key stroke.
- public boolean isHandledKey(java.awt.event.KeyEvent e) {
- if (0 == _maskLength) // if no mask, let component handle all keys
- return false;
- char c = e.getKeyChar();
- if (Character.isISOControl(c)) {
- int k = e.getKeyCode();
- switch (k) {
- case e.VK_LEFT:
- case e.VK_RIGHT:
- case e.VK_HOME:
- case e.VK_END:
- return !e.isShiftDown();
- }
- switch (c) {
- case e.VK_BACK_SPACE:
- case e.VK_DELETE:
- return true;
- default:
- return false;
- }
- }
- return true;
- }
-
- public String cut(String data, int selStart, int selEnd, StringBuffer newData) {
- newData.append(data);
- setMaskPos(selStart);
- clearSelectedText(selStart, selEnd, newData);
- return data.substring(selStart, selEnd);
- }
-
- public int paste(String data, String pasteData, int pos, StringBuffer newData, int selStart, int selEnd) {
- boolean hasSelection = selStart < selEnd;
- if (hasSelection)
- pos = selStart;
- if (!setMaskPos(pos))
- return -1;
- StringBuffer sb = new StringBuffer(data);
- int len = pasteData.length();
- KeyEvent e = new KeyEvent(new TextField(), 0, 0, 0, 0);
- String s = new String(sb.toString());
- int newpos = pos;
- for (int i = 0; i < len; i++) {
- char c = pasteData.charAt(i);
- e.setKeyChar(c);
- e.setKeyCode(c);
- sb = new StringBuffer();
- int selstart = 0, selend = 0;
- if (hasSelection && newpos >= selStart && newpos < selEnd) {
- if (i == 0) {
- selstart = selStart;
- selend = selEnd;
- } else {
- selstart = newpos;
- selend = newpos + 1;
- }
- }
- newpos = processKey(e, newpos, s, sb, selstart, selend);
- if (newpos == -1 || newpos == -2) {
- break; // next paste character was rejected by mask
- }
- s = sb.toString();
- }
- newData.append(s);
- return newpos;
- }
-
- boolean clearSelectedText(int selStart, int selEnd, StringBuffer newData) {
- if (selEnd > selStart) { // only if selected text exists
- // save mask scan state
- int curMaskPos = _curMaskPos;
- int nextMaskPos = _nextMaskPos;
- int curMaskType = _curMaskType;
- char curMaskChar = _curMaskChar;
- char lastMaskChar = _lastMaskChar;
- char decimalPoint = _decimalPoint;
- int commandCorrection = _commandCorrection;
- int shiftState = _shiftState;
-
- setMaskPos(selStart);
- int outpos = selStart; // track input position
- char c = _curMaskChar;
- while (outpos < selEnd && c != 0) {
- if (_curMaskType == filtertype)
- c = '_';
- newData.setCharAt(outpos++, c);
- c = getNextMaskChar();
- }
-
- // restore mask scan state
- _curMaskPos = curMaskPos;
- _nextMaskPos = nextMaskPos;
- _curMaskType = curMaskType;
- _curMaskChar = curMaskChar;
- _lastMaskChar = lastMaskChar;
- _decimalPoint = decimalPoint;
- _commandCorrection = commandCorrection;
- _shiftState = shiftState;
- return true;
- }
- return false;
- }
-
- // Set new position in mask. Return false iff it is out of bounds.
- boolean setMaskPos(int pos) {
- if (pos < 0)
- return false;
- resetMaskScan();
- _inRange = true;
- while (pos-- >= 0)
- if (getNextMaskChar() == 0)
- return _inRange = false;
- return true;
- }
-
- // Returns the position in the mask of the first filter that follows the
- // current position.
- int nextFilterPos() {
- while (getNextMaskChar() != 0)
- if (_curMaskType == filtertype)
- return _curMaskPos - _commandCorrection;
- return -1;
- }
-
- // Returns the position in the mask of the filter that precedes the
- // current position. Return of -1 means no previous filter was found.
- int prevFilterPos() {
- int oldPos = _curMaskPos;
- resetMaskScan();
- int prevFilterPos = -1;
- int prevCommandCorrection = 0;
- while (nextFilterPos() != -1) {
- if (_curMaskPos >= oldPos) { // scanned past previous filter
- if (!_inRange)
- return _curMaskPos - _commandCorrection;
- break;
- }
- prevFilterPos = _curMaskPos; // save pos info on this filter
- prevCommandCorrection = _commandCorrection;
- }
- return prevFilterPos - prevCommandCorrection;
- }
-
- // Return true iff key is accepted by current filter
- boolean matchFilter(char key) {
- switch (_curMaskChar) {
- case '0': // any digit
- return Character.isDigit(key);
- case '9': // any digit or space
- return Character.isDigit(key) || Character.isSpaceChar(key);
- case '#': // digit, space or sign
- return Character.isDigit(key) || Character.isSpaceChar(key) ||
- key == '+' || key == '-';
- case 'L': // letter
- case '?':
- return Character.isLetter(key);
- case 'A': // letter or digit
- case 'a':
- return Character.isLetterOrDigit(key);
- case '&': // any character
- case 'C':
- return true;
- default:
- return false;
- }
- }
-
- // Prepare mask to be scanned from the beginning.
- void resetMaskScan() {
- _curMaskPos = 0;
- _nextMaskPos = 0; // set first char to get
- _lastMaskChar = 0;
- _commandCorrection = 0;
- _curMaskType = 0;
- _shiftState = eShiftNul;
- }
-
- char getNextMaskChar() {
- while (true) { // loop through command chars
- if (_nextMaskPos >= _maskLength)
- return 0;
- _lastMaskChar = _curMaskChar;
- _curMaskChar = _mask.charAt(_nextMaskPos++);
- if (isCommand()) {
- _commandCorrection++;
- if ('<' == _curMaskChar)
- _shiftState = eShiftLower;
- else if ('>' == _curMaskChar)
- _shiftState = eShiftUpper;
- } else
- break;
- }
- _curMaskType = literaltype;
- if (_lastMaskChar != '\\' && isFilter())
- _curMaskType = filtertype;
- _curMaskPos = _nextMaskPos - 1;
- return _curMaskChar;
- }
-
- // Return true iff key is a cursor navigation key.
- boolean isNavKey(int key) {
- switch (key) {
- default:
- return false;
- case java.awt.event.KeyEvent.VK_LEFT:
- case java.awt.event.KeyEvent.VK_RIGHT:
- case java.awt.event.KeyEvent.VK_BACK_SPACE:
- case java.awt.event.KeyEvent.VK_HOME:
- case java.awt.event.KeyEvent.VK_END:
- }
- return true;
- }
-
- boolean isFilter() { return -1 != "09#aAL?&C".indexOf(_curMaskChar); }
- boolean isCommand() { return -1 != "<>\\" .indexOf(_curMaskChar); }
- boolean isMandatory() { return -1 != "0LA&" .indexOf(_curMaskChar); }
-
- // Called by all ctors
- void init() {}
-
- // Variables
- String _mask = "" ; // current mask
- int _maskLength = 0 ; // its length
- int _datatype = texttype; // data type of masked field
- int _filterCount = 0 ; // count of filters in mask
- int _maskCount = 0 ; // count of displayable characters
- int _firstFilterPos = 0 ;
- int _lastFilterPos = 0 ;
- int[] _filterPositions ; // track all filter positions
- int[] _commandCorrections ; // track all command corrections
- boolean _inRange = true ; // true if last mask position request is in range
-
- // State variables for scanning the mask
- int _curMaskPos = 0; // position of current mask character
- int _nextMaskPos = 0; // position of next mask character
- int _curMaskType = nulltype; // type of current mask character
- char _curMaskChar = 0;
- char _lastMaskChar = 0;
- int _commandCorrection = 0;
- int _shiftState = eShiftNul;
- char _decimalPoint = '.';
-
- // Internal mask command type codes
- static final int nulltype = 0;
- static final int filtertype = 1;
- static final int literaltype = 2;
-
- // Internal shift states
- static final int eShiftNul = 0;
- static final int eShiftUpper = 1;
- static final int eShiftLower = 2;
- }
-